import logging
import os
import signal
import sys
import time

import cv2

# 添加src目录到Python路径
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))

from src.utils.config_loader import ConfigLoader
from src.utils.frame_utils import FrameUtils
from src.core.detector import YOLODetector
from src.core.stream_processor import RTSPStreamProcessor
from src.core.visualizer import Visualizer
from src.services.web_server import WebServer
from src.services.redis_publisher import RedisPublisher


class YOLOv8StreamApp:
    """YOLOv8流处理应用主类"""

    def __init__(self):
        """初始化应用"""
        # 设置日志
        self._setup_logging()

        # 加载配置（app、model、redis）
        self.config_loader = ConfigLoader()
        self.app_config = self.config_loader.get_app_config()
        self.model_config = self.config_loader.get_model_config()

        # 初始化组件（检测器、流处理器、可视化器、Web服务、Redis发布器等）
        self.detector = None
        self.stream_processor = None
        self.visualizer = None
        self.web_server = None
        self.redis_publisher = None
        self.frame_utils = FrameUtils()

        # 运行标志
        self.running = False

        # 性能统计
        self.frame_count = 0
        self.start_time = time.time()

        logging.info("YOLOv8StreamApp初始化完成")

    def _setup_logging(self):
        """设置日志配置"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            handlers=[
                logging.StreamHandler(),
                logging.FileHandler('app.log')
            ]
        )

    def initialize_components(self):
        """初始化所有组件"""
        try:
            # 初始化YOLO检测器
            self.detector = YOLODetector(self.model_config)

            # 初始化可视化器
            self.visualizer = Visualizer(self.app_config['visualization'])

            # 初始化RTSP流处理器
            rtsp_url = self.app_config['rtsp']['url']
            self.stream_processor = RTSPStreamProcessor(rtsp_url, self.app_config)

            # 初始化Web服务器
            self.web_server = WebServer(self.app_config['server'])

            # 初始化Redis发布器（可选）
            try:
                redis_config = self.config_loader.get_redis_config()
                self.redis_publisher = RedisPublisher(redis_config)
            except Exception as e:
                logging.warning(f"Redis初始化失败，将跳过Redis功能: {e}")
                self.redis_publisher = None

            logging.info("所有组件初始化完成")
            return True

        except Exception as e:
            logging.error(f"组件初始化失败: {e}")
            return False

    def process_frame(self, frame):
        """
        处理单个帧
        Args:
            frame: 输入帧
        """
        try:
            # 执行目标检测
            detections = self.detector.detect(frame)

            # 过滤检测结果
            confidence_threshold = self.app_config['detection']['confidence_threshold']
            filtered_detections = self.detector.filter_detections(
                detections, confidence_threshold
            )

            # 绘制检测结果
            annotated_frame = self.visualizer.draw_detections(frame, filtered_detections)

            # 添加信息覆盖层
            current_time = time.time()
            fps = self.frame_utils.calculate_fps(
                self.frame_count, self.start_time, current_time
            )

            info = {
                'fps': fps,
                'detection_count': len(filtered_detections)
            }

            final_frame = self.visualizer.add_info_overlay(annotated_frame, info)

            # 编码帧为JPEG
            encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 85]
            _, buffer = cv2.imencode('.jpg', final_frame, encode_param)

            # 更新Web服务器的帧数据
            if self.web_server:
                self.web_server.update_frame(buffer.tobytes())

            # 发布到Redis（如果可用）
            if self.redis_publisher:
                frame_base64 = self.frame_utils.frame_to_base64(final_frame)
                self.redis_publisher.publish_frame(frame_base64, filtered_detections)

            # 更新统计
            self.frame_count += 1

            # 每100帧打印一次统计信息
            if self.frame_count % 100 == 0:
                logging.info(f"已处理 {self.frame_count} 帧，当前FPS: {fps:.2f}")

        except Exception as e:
            logging.error(f"处理帧时出错: {e}")

    def start(self):
        """启动应用"""
        if not self.initialize_components():
            logging.error("组件初始化失败，程序退出")
            return

        # 启动Web服务器
        if self.web_server:
            self.web_server.start_server_thread()
            time.sleep(1)  # 等待服务器启动

        # 启动RTSP流处理
        self.running = True
        self.stream_processor.start_capture(self.process_frame)

        # 主循环
        try:
            logging.info("应用启动成功，开始处理视频流...")
            logging.info(
                f"Web界面访问地址: http://{self.app_config['server']['host']}:{self.app_config['server']['port']}")

            while self.running:
                time.sleep(1)

                # 检查流处理器状态
                if not self.stream_processor.running:
                    logging.warning("流处理器已停止")
                    break

        except KeyboardInterrupt:
            logging.info("收到退出信号...")
        except Exception as e:
            logging.error(f"运行时错误: {e}")
        finally:
            self.stop()

    def stop(self):
        """停止应用"""
        logging.info("正在停止应用...")

        self.running = False

        # 停止流处理器
        if self.stream_processor:
            self.stream_processor.stop()

        # 关闭Redis连接
        if self.redis_publisher:
            self.redis_publisher.close()

        logging.info("应用已停止")

    def signal_handler(self, signum, frame):
        """信号处理器"""
        logging.info(f"收到信号 {signum}，准备退出...")
        self.stop()
        sys.exit(0)


def main():
    """主函数"""
    # 检查模型文件是否存在
    model_path = "models/yolov8n.pt"
    if not os.path.exists(model_path):
        logging.error(f"模型文件不存在: {model_path}")
        logging.info("请下载YOLOv8模型文件到models目录")
        return

    # 创建应用实例
    app = YOLOv8StreamApp()

    # 注册信号处理器
    signal.signal(signal.SIGINT, app.signal_handler)
    signal.signal(signal.SIGTERM, app.signal_handler)

    # 启动应用
    app.start()


if __name__ == "__main__":
    main()